﻿// Decompiled with JetBrains decompiler
// Type: TaleWorlds.CampaignSystem.MapEvents.MapEventSide
// Assembly: TaleWorlds.CampaignSystem, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: E85F8C15-4DF6-4E9C-A58A-29177E40D07A
// Assembly location: D:\steam\steamapps\common\Mount & Blade II Bannerlord\bin\Win64_Shipping_Client\TaleWorlds.CampaignSystem.dll

using Helpers;
using System;
using System.Collections.Generic;
using System.Linq;
using TaleWorlds.CampaignSystem.Actions;
using TaleWorlds.CampaignSystem.CharacterDevelopment;
using TaleWorlds.CampaignSystem.Party;
using TaleWorlds.CampaignSystem.Roster;
using TaleWorlds.CampaignSystem.Settlements;
using TaleWorlds.CampaignSystem.Siege;
using TaleWorlds.Core;
using TaleWorlds.Library;
using TaleWorlds.Localization;
using TaleWorlds.SaveSystem;

#nullable disable
namespace TaleWorlds.CampaignSystem.MapEvents
{
  public class MapEventSide
  {
    [CachedData]
    private List<(FlattenedTroopRosterElement, MapEventParty, float)> _readyTroopsPriorityList;
    [CachedData]
    private Dictionary<UniqueTroopDescriptor, MapEventParty> _readyTroopsTemporaryCache;
    [CachedData]
    private bool _requiresTroopCacheUpdate;
    [CachedData]
    private Dictionary<UniqueTroopDescriptor, MapEventParty> _allocatedTroops;
    [CachedData]
    internal float LeaderSimulationModifier;
    [SaveableField(30)]
    private readonly MBList<MapEventParty> _battleParties;
    [SaveableField(9)]
    public float StrengthRatio = 1f;
    [SaveableField(10)]
    public float RenownValue;
    [SaveableField(11)]
    public float InfluenceValue;
    public float RenownAtMapEventEnd;
    [SaveableField(14)]
    public int Casualties;
    [SaveableField(16)]
    private readonly MapEvent _mapEvent;
    [CachedData]
    private List<UniqueTroopDescriptor> _simulationTroopList;
    [SaveableField(130)]
    private IFaction _mapFaction;
    [SaveableField(23)]
    private int _selectedSimulationTroopIndex;
    [SaveableField(24)]
    private UniqueTroopDescriptor _selectedSimulationTroopDescriptor;
    [SaveableField(25)]
    private CharacterObject _selectedSimulationTroop;
    private TroopUpgradeTracker _troopUpgradeTracker;
    [SaveableField(26)]
    internal bool IsSurrendered;
    [SaveableField(27)]
    private MBList<MobileParty> _nearbyPartiesAddedToPlayerMapEvent = new MBList<MobileParty>();

    internal static void AutoGeneratedStaticCollectObjectsMapEventSide(
      object o,
      List<object> collectedObjects)
    {
      ((MapEventSide) o).AutoGeneratedInstanceCollectObjects(collectedObjects);
    }

    protected virtual void AutoGeneratedInstanceCollectObjects(List<object> collectedObjects)
    {
      collectedObjects.Add((object) this._battleParties);
      collectedObjects.Add((object) this._mapEvent);
      collectedObjects.Add((object) this._mapFaction);
      UniqueTroopDescriptor.AutoGeneratedStaticCollectObjectsUniqueTroopDescriptor((object) this._selectedSimulationTroopDescriptor, collectedObjects);
      collectedObjects.Add((object) this._selectedSimulationTroop);
      collectedObjects.Add((object) this._nearbyPartiesAddedToPlayerMapEvent);
      collectedObjects.Add((object) this.LeaderParty);
    }

    internal static object AutoGeneratedGetMemberValueLeaderParty(object o)
    {
      return (object) ((MapEventSide) o).LeaderParty;
    }

    internal static object AutoGeneratedGetMemberValueMissionSide(object o)
    {
      return (object) ((MapEventSide) o).MissionSide;
    }

    internal static object AutoGeneratedGetMemberValueCasualtyStrength(object o)
    {
      return (object) ((MapEventSide) o).CasualtyStrength;
    }

    internal static object AutoGeneratedGetMemberValueStrengthRatio(object o)
    {
      return (object) ((MapEventSide) o).StrengthRatio;
    }

    internal static object AutoGeneratedGetMemberValueRenownValue(object o)
    {
      return (object) ((MapEventSide) o).RenownValue;
    }

    internal static object AutoGeneratedGetMemberValueInfluenceValue(object o)
    {
      return (object) ((MapEventSide) o).InfluenceValue;
    }

    internal static object AutoGeneratedGetMemberValueCasualties(object o)
    {
      return (object) ((MapEventSide) o).Casualties;
    }

    internal static object AutoGeneratedGetMemberValueIsSurrendered(object o)
    {
      return (object) ((MapEventSide) o).IsSurrendered;
    }

    internal static object AutoGeneratedGetMemberValue_battleParties(object o)
    {
      return (object) ((MapEventSide) o)._battleParties;
    }

    internal static object AutoGeneratedGetMemberValue_mapEvent(object o)
    {
      return (object) ((MapEventSide) o)._mapEvent;
    }

    internal static object AutoGeneratedGetMemberValue_mapFaction(object o)
    {
      return (object) ((MapEventSide) o)._mapFaction;
    }

    internal static object AutoGeneratedGetMemberValue_selectedSimulationTroopIndex(object o)
    {
      return (object) ((MapEventSide) o)._selectedSimulationTroopIndex;
    }

    internal static object AutoGeneratedGetMemberValue_selectedSimulationTroopDescriptor(object o)
    {
      return (object) ((MapEventSide) o)._selectedSimulationTroopDescriptor;
    }

    internal static object AutoGeneratedGetMemberValue_selectedSimulationTroop(object o)
    {
      return (object) ((MapEventSide) o)._selectedSimulationTroop;
    }

    internal static object AutoGeneratedGetMemberValue_nearbyPartiesAddedToPlayerMapEvent(object o)
    {
      return (object) ((MapEventSide) o)._nearbyPartiesAddedToPlayerMapEvent;
    }

    [SaveableProperty(4)]
    public PartyBase LeaderParty { get; internal set; }

    public MBReadOnlyList<MapEventParty> Parties
    {
      get => (MBReadOnlyList<MapEventParty>) this._battleParties;
    }

    [SaveableProperty(7)]
    public BattleSideEnum MissionSide { get; private set; }

    private IBattleObserver BattleObserver => this._mapEvent.BattleObserver;

    public int TroopCount => this.RecalculateMemberCountOfSide();

    public int CountTroops(Func<FlattenedTroopRosterElement, bool> pred)
    {
      int num = 0;
      foreach (MapEventParty battleParty in (List<MapEventParty>) this._battleParties)
      {
        foreach (FlattenedTroopRosterElement troop in battleParty.Troops)
        {
          if (pred(troop))
            ++num;
        }
      }
      return num;
    }

    public int GetTotalHealthyTroopCountOfSide()
    {
      int troopCountOfSide = 0;
      foreach (MapEventParty battleParty in (List<MapEventParty>) this._battleParties)
        troopCountOfSide += battleParty.Party.MemberRoster.TotalHealthyCount;
      return troopCountOfSide;
    }

    public int GetTotalHealthyHeroCountOfSide()
    {
      int healthyHeroCountOfSide = 0;
      foreach (MapEventParty battleParty in (List<MapEventParty>) this._battleParties)
      {
        TroopRoster memberRoster = battleParty.Party.MemberRoster;
        healthyHeroCountOfSide += memberRoster.TotalHeroes - memberRoster.TotalWoundedHeroes;
      }
      return healthyHeroCountOfSide;
    }

    public int NumRemainingSimulationTroops
    {
      get
      {
        List<UniqueTroopDescriptor> simulationTroopList = this._simulationTroopList;
        return simulationTroopList == null ? 0 : __nonvirtual (simulationTroopList.Count);
      }
    }

    [SaveableProperty(15)]
    public float CasualtyStrength { get; private set; }

    public MapEvent MapEvent => this._mapEvent;

    public MapEventSide OtherSide
    {
      get
      {
        return this._mapEvent.GetMapEventSide(this.MissionSide == BattleSideEnum.Defender ? BattleSideEnum.Attacker : BattleSideEnum.Defender);
      }
    }

    public IFaction MapFaction => this._mapFaction ?? this.LeaderParty.MapFaction;

    internal MapEventSide(MapEvent mapEvent, BattleSideEnum missionSide, PartyBase leaderParty)
    {
      this._mapEvent = mapEvent;
      this.LeaderParty = leaderParty;
      this.MissionSide = missionSide;
      this._mapFaction = leaderParty.MapFaction;
      this._battleParties = new MBList<MapEventParty>();
    }

    internal void CacheLeaderSimulationModifier()
    {
      this.LeaderSimulationModifier = Campaign.Current.Models.MilitaryPowerModel.GetLeaderModifierInMapEvent(this._mapEvent, this.MissionSide);
    }

    internal void AddPartyInternal(PartyBase party)
    {
      this._battleParties.Add(new MapEventParty(party));
      this._mapEvent.AddInvolvedPartyInternal(party, this.MissionSide);
    }

    internal void RemovePartyInternal(PartyBase party)
    {
      this._battleParties.RemoveAt(this._battleParties.FindIndexQ<MapEventParty>((Func<MapEventParty, bool>) (p => p.Party == party)));
      this._mapEvent.RemoveInvolvedPartyInternal(party);
      if (this.LeaderParty != party)
        return;
      this._mapFaction = this.LeaderParty.MapFaction;
      if (this._battleParties.Count > 0)
      {
        this.LeaderParty = this._battleParties[0].Party;
        this.CacheLeaderSimulationModifier();
      }
      else
        this.MapEvent.FinalizeEvent();
    }

    public int RecalculateMemberCountOfSide()
    {
      int num = 0;
      foreach (MapEventParty party in (List<MapEventParty>) this.Parties)
        num += party.Party.NumberOfHealthyMembers;
      return num;
    }

    public float RecalculateStrengthOfSide()
    {
      float num = 0.0f;
      foreach (MapEventParty party in (List<MapEventParty>) this.Parties)
        num += party.Party.TotalStrength;
      return num;
    }

    internal void DistributeLootAmongWinners(LootCollector lootCollector)
    {
      int totalContribution = this.CalculateTotalContribution();
      lootCollector.MakeFreedHeroesEscape(lootCollector.LootedPrisoners, this.MapEvent.IsPlayerMapEvent && this.MapEvent.PlayerSide == this.MapEvent.WinningSide);
      if ((double) totalContribution > 9.9999997473787516E-06)
      {
        MapEventParty[] array = new MapEventParty[this._battleParties.Count];
        this._battleParties.CopyTo(array);
        foreach (MapEventParty partyRec in array)
        {
          int giveShareToParty = this.CalculateContributionAndGiveShareToParty(lootCollector, partyRec, totalContribution);
          totalContribution -= giveShareToParty;
        }
      }
      lootCollector.MakeRemainingPrisonerHeroesEscape();
    }

    private int CalculateContributionAndGiveShareToParty(
      LootCollector lootCollector,
      MapEventParty partyRec,
      int totalContribution)
    {
      if (partyRec.Party.MemberRoster.Count <= 0)
        return 0;
      float lootAmount = (float) partyRec.ContributionToBattle / (float) totalContribution;
      lootCollector.GiveShareOfLootToParty(partyRec.RosterToReceiveLootMembers, partyRec.RosterToReceiveLootPrisoners, partyRec.RosterToReceiveLootItems, partyRec.Party, lootAmount, this._mapEvent);
      return partyRec.ContributionToBattle;
    }

    public bool IsMainPartyAmongParties()
    {
      return this.Parties.AnyQ<MapEventParty>((Func<MapEventParty, bool>) (party => party.Party == PartyBase.MainParty));
    }

    public float GetPlayerPartyContributionRate()
    {
      int totalContribution = this.CalculateTotalContribution();
      if (totalContribution == 0)
        return 0.0f;
      int num = 0;
      foreach (MapEventParty battleParty in (List<MapEventParty>) this._battleParties)
      {
        if (battleParty.Party == PartyBase.MainParty)
        {
          num = battleParty.ContributionToBattle;
          break;
        }
      }
      return (float) num / (float) totalContribution;
    }

    internal int CalculateTotalContribution()
    {
      int totalContribution = 0;
      foreach (MapEventParty battleParty in (List<MapEventParty>) this._battleParties)
      {
        if (battleParty.Party.MemberRoster.Count > 0)
          totalContribution += battleParty.ContributionToBattle;
      }
      return totalContribution;
    }

    public void CalculateRenownAndInfluenceValues(float[] strengthOfSide)
    {
      int missionSide = (int) this.MissionSide;
      int oppositeSide = (int) this.MissionSide.GetOppositeSide();
      float x = 1f;
      float num1 = 1f;
      if (this._mapEvent.IsSiegeAssault)
      {
        float settlementAdvantage = Campaign.Current.Models.CombatSimulationModel.GetSettlementAdvantage(this._mapEvent.MapEventSettlement);
        if (this.MissionSide == BattleSideEnum.Defender)
          num1 = settlementAdvantage;
        else
          x = settlementAdvantage;
      }
      float num2 = this._mapEvent.IsSiegeAssault ? 0.7f : (this._mapEvent.IsSallyOut || this._mapEvent.IsRaid || this._mapEvent.MapEventSettlement != null ? 0.6f : 0.5f);
      this.StrengthRatio = (float) (((double) strengthOfSide[oppositeSide] * (double) MathF.Sqrt(x) + 10.0) / ((double) strengthOfSide[missionSide] * (double) num1 + 10.0));
      this.StrengthRatio = (double) this.StrengthRatio > 10.0 ? 10f : this.StrengthRatio;
      if ((double) strengthOfSide[missionSide] <= 0.0)
        return;
      this.RenownValue = (float) ((double) MathF.Pow(strengthOfSide[oppositeSide] * MathF.Sqrt(x), 0.75f) * (double) MathF.Pow(this.StrengthRatio, 0.45f) * (double) num2 * 0.75);
      this.InfluenceValue = (float) ((double) MathF.Pow(strengthOfSide[oppositeSide] * MathF.Sqrt(x), 0.75f) * (double) MathF.Pow(this.StrengthRatio, 0.15f) * 0.60000002384185791);
    }

    internal void CommitXpGains()
    {
      foreach (MapEventParty battleParty in (List<MapEventParty>) this._battleParties)
        battleParty.CommitXpGain();
    }

    public virtual void DistributeRenownAndInfluence(
      MapEventResultExplainer resultExplainers = null,
      bool forScoreboard = false)
    {
      int totalContribution = this.CalculateTotalContribution();
      float renownValue = this.RenownValue;
      float influenceValue = this.InfluenceValue;
      List<MobileParty> mobilePartyList1 = new List<MobileParty>();
      List<MobileParty> mobilePartyList2 = new List<MobileParty>();
      foreach (MapEventParty battleParty in (List<MapEventParty>) this._battleParties)
      {
        PartyBase party = battleParty.Party;
        if (party.IsMobile && party.MobileParty.IsVillager)
          mobilePartyList1.Add(party.MobileParty);
        if (party.IsMobile && party.MobileParty.IsCaravan)
          mobilePartyList2.Add(party.MobileParty);
      }
      foreach (MapEventParty battleParty in (List<MapEventParty>) this._battleParties)
      {
        PartyBase party1 = battleParty.Party;
        if (totalContribution > 0)
        {
          float contributionShare = (float) battleParty.ContributionToBattle / (float) totalContribution;
          ExplainedNumber explainedNumber1 = new ExplainedNumber(includeDescriptions: true);
          ExplainedNumber explainedNumber2 = new ExplainedNumber(includeDescriptions: true);
          ExplainedNumber explainedNumber3 = new ExplainedNumber(includeDescriptions: true);
          explainedNumber3 = Campaign.Current.Models.BattleRewardModel.CalculateMoraleGainVictory(party1, renownValue, contributionShare);
          battleParty.MoraleChange = explainedNumber3.ResultNumber;
          if (resultExplainers != null && Hero.MainHero == party1.LeaderHero)
            resultExplainers.MoraleExplainedNumber = explainedNumber3;
          if (party1.LeaderHero != null)
          {
            foreach (MobileParty mobileParty in mobilePartyList1)
            {
              if (mobileParty.HomeSettlement.OwnerClan != party1.LeaderHero.Clan && !mobileParty.HomeSettlement.OwnerClan.IsEliminated && !party1.LeaderHero.Clan.IsEliminated)
              {
                int relationChange1 = MBRandom.RoundRandomized(4f * contributionShare);
                if (relationChange1 > 0)
                  ChangeRelationAction.ApplyRelationChangeBetweenHeroes(mobileParty.HomeSettlement.OwnerClan.Leader, party1.LeaderHero.Clan.Leader, relationChange1);
                int relationChange2 = MBRandom.RoundRandomized(2f * contributionShare);
                foreach (Hero notable in (List<Hero>) mobileParty.HomeSettlement.Notables)
                  ChangeRelationAction.ApplyRelationChangeBetweenHeroes(notable, party1.LeaderHero.Clan.Leader, relationChange2);
              }
            }
            foreach (MobileParty mobileParty in mobilePartyList2)
            {
              if (mobileParty.HomeSettlement != null && mobileParty.HomeSettlement.OwnerClan != null && party1.LeaderHero != null && mobileParty.HomeSettlement.OwnerClan.Leader.Clan != party1.LeaderHero.Clan && mobileParty.Party.Owner != null && mobileParty.Party.Owner != Hero.MainHero && mobileParty.Party.Owner.IsAlive && party1.LeaderHero.Clan.Leader != null && party1.LeaderHero.Clan.Leader.IsAlive && !mobileParty.IsCurrentlyUsedByAQuest)
              {
                int relationChange = MBRandom.RoundRandomized(6f * contributionShare);
                ChangeRelationAction.ApplyRelationChangeBetweenHeroes(mobileParty.Party.Owner, party1.LeaderHero.Clan.Leader, relationChange);
              }
            }
            if (this.MapEvent.IsRaid && this.MissionSide == BattleSideEnum.Defender && this == this.MapEvent.Winner)
              ChangeRelationAction.ApplyRelationChangeBetweenHeroes(this.MapEvent.MapEventSettlement.Notables.GetRandomElement<Hero>(), party1.LeaderHero, 5);
          }
          if (party1.LeaderHero != null)
          {
            explainedNumber1 = Campaign.Current.Models.BattleRewardModel.CalculateRenownGain(party1, renownValue, contributionShare);
            explainedNumber2 = Campaign.Current.Models.BattleRewardModel.CalculateInfluenceGain(party1, influenceValue, contributionShare);
            float num1 = this._mapEvent.StrengthOfSide[(int) this.MissionSide.GetOppositeSide()];
            float num2 = num1;
            foreach (MapEventParty party2 in (List<MapEventParty>) this.OtherSide.Parties)
              num2 -= party2.Party.TotalStrength;
            battleParty.GainedRenown = explainedNumber1.ResultNumber * num2 / num1;
            battleParty.GainedInfluence = explainedNumber2.ResultNumber * num2 / num1;
            if (resultExplainers != null && Hero.MainHero == party1.LeaderHero)
            {
              resultExplainers.RenownExplainedNumber = new ExplainedNumber(battleParty.GainedRenown);
              resultExplainers.InfluenceExplainedNumber = new ExplainedNumber(battleParty.GainedInfluence);
            }
          }
        }
      }
      if (!forScoreboard)
      {
        this._mapEvent.RecalculateStrengthOfSides();
        this.CalculateRenownAndInfluenceValues(this._mapEvent.StrengthOfSide);
      }
      else
        this.RenownAtMapEventEnd = this.RenownValue;
    }

    public void ApplyRenownAndInfluenceChanges()
    {
      foreach (MapEventParty battleParty in (List<MapEventParty>) this._battleParties)
      {
        PartyBase party = battleParty.Party;
        Hero hero = party == PartyBase.MainParty ? Hero.MainHero : party.LeaderHero;
        if (hero != null)
        {
          if ((double) battleParty.GainedRenown > 1.0 / 1000.0)
          {
            GainRenownAction.Apply(hero, battleParty.GainedRenown, true);
            battleParty.GainedRenown = 0.0f;
          }
          if ((double) battleParty.GainedInfluence > 1.0 / 1000.0)
          {
            GainKingdomInfluenceAction.ApplyForBattle(hero, battleParty.GainedInfluence);
            battleParty.GainedInfluence = 0.0f;
          }
        }
      }
    }

    public void ApplyFinalRewardsAndChanges()
    {
      foreach (MapEventParty battleParty in (List<MapEventParty>) this._battleParties)
      {
        PartyBase party = battleParty.Party;
        Hero hero = party == PartyBase.MainParty ? Hero.MainHero : party.LeaderHero;
        if (party.MobileParty != null)
          party.MobileParty.RecentEventsMorale += battleParty.MoraleChange;
        if (hero != null)
        {
          if ((double) battleParty.PlunderedGold > 1.0 / 1000.0)
          {
            if (hero == Hero.MainHero)
            {
              MBTextManager.SetTextVariable("GOLD", battleParty.PlunderedGold);
              MBInformationManager.AddQuickInformation(GameTexts.FindText("str_plunder_gain_message"));
            }
            GiveGoldAction.ApplyBetweenCharacters((Hero) null, hero, battleParty.PlunderedGold, true);
          }
          if ((double) battleParty.GoldLost > 1.0 / 1000.0)
            GiveGoldAction.ApplyBetweenCharacters(hero, (Hero) null, battleParty.GoldLost, true);
        }
        else if (party.IsMobile && party.MobileParty.IsPartyTradeActive)
        {
          party.MobileParty.PartyTradeGold -= battleParty.GoldLost;
          party.MobileParty.PartyTradeGold += battleParty.PlunderedGold;
        }
      }
    }

    public virtual void CalculatePlunderedGoldShare(
      float totalPlunderedGold,
      MapEventResultExplainer resultExplainers = null)
    {
      int totalContribution = this.CalculateTotalContribution();
      foreach (MapEventParty battleParty in (List<MapEventParty>) this._battleParties)
      {
        if (totalContribution > 0)
        {
          double num1 = (double) battleParty.ContributionToBattle / (double) totalContribution;
          totalContribution -= battleParty.ContributionToBattle;
          double num2 = (double) totalPlunderedGold;
          int num3 = (int) (num1 * num2);
          totalPlunderedGold -= (float) num3;
          battleParty.PlunderedGold = num3;
        }
      }
    }

    public void UpdatePartiesMoveState()
    {
      foreach (MapEventParty party in (List<MapEventParty>) this.Parties)
      {
        if (party.Party.IsMobile && party.Party.MobileParty.IsActive && party.Party.MobileParty.CurrentSettlement == null && (this._mapEvent.IsRaid && (double) this._mapEvent.MapEventSettlement.SettlementHitPoints <= 0.0 || this._mapEvent.IsSiegeAssault) && party.Party.MobileParty.Army != null && party.Party.MobileParty.Army.AiBehaviorObject == this._mapEvent.MapEventSettlement)
          party.Party.MobileParty.Army.AIBehavior = Army.AIBehaviorFlags.Unassigned;
      }
    }

    public void HandleMapEventEnd()
    {
      while (this.Parties.Count > 0)
        this.HandleMapEventEndForPartyInternal((this.Parties.FirstOrDefault<MapEventParty>((Func<MapEventParty, bool>) (x => !x.Party.IsMobile || x.Party.MobileParty.Army == null || x.Party.MobileParty.Army.LeaderParty != x.Party.MobileParty)) ?? this.Parties[this.Parties.Count - 1]).Party);
    }

    internal void HandleMapEventEndForPartyInternal(PartyBase party)
    {
      IEnumerable<TroopRosterElement> troopRosterElements = party.MemberRoster.GetTroopRoster().WhereQ<TroopRosterElement>((Func<TroopRosterElement, bool>) (x => x.Character.IsHero && x.Character.HeroObject.IsAlive && x.Character.HeroObject.DeathMark == KillCharacterAction.KillCharacterActionDetail.DiedInBattle));
      PartyBase leaderParty = this._mapEvent.GetLeaderParty(party.OpponentSide);
      bool flag = this._mapEvent.IsWinnerSide(party.Side);
      bool attackersRanAway = this._mapEvent.AttackersRanAway;
      party.MapEventSide = (MapEventSide) null;
      foreach (TroopRosterElement troopRosterElement in troopRosterElements)
        KillCharacterAction.ApplyByBattle(troopRosterElement.Character.HeroObject, this.OtherSide.LeaderParty.LeaderHero);
      if (party.IsMobile && (party.NumberOfAllMembers == 0 || !flag && !attackersRanAway && (party.NumberOfHealthyMembers == 0 || this._mapEvent.BattleState != BattleState.None && party.MobileParty.IsMilitia) && (party.MobileParty.Army == null || party.MobileParty.Army.LeaderParty.Party.NumberOfHealthyMembers == 0)) && party != PartyBase.MainParty && party.IsActive && (!party.MobileParty.IsDisbanding || party.MemberRoster.Count == 0))
        DestroyPartyAction.Apply(leaderParty, party.MobileParty);
      party.MemberRoster.RemoveZeroCounts();
      party.PrisonRoster.RemoveZeroCounts();
      if (!party.IsMobile || !party.MobileParty.IsActive || party.MobileParty.CurrentSettlement != null)
        return;
      party.SetVisualAsDirty();
    }

    public void AddHeroDamage(Hero character, int damage) => character.HitPoints -= damage;

    public void AllocateTroops(
      ref List<UniqueTroopDescriptor> troopsList,
      int number = -1,
      Func<UniqueTroopDescriptor, MapEventParty, bool> customAllocationConditions = null)
    {
      if (troopsList == null)
        troopsList = new List<UniqueTroopDescriptor>();
      else
        troopsList.Clear();
      int num1 = number >= 0 ? number : 100000000;
      while (num1 > 0)
      {
        int index1 = -1;
        float num2 = float.MinValue;
        for (int index2 = 0; index2 < this._readyTroopsPriorityList.Count; ++index2)
        {
          if ((double) this._readyTroopsPriorityList[index2].Item3 > (double) num2)
          {
            num2 = this._readyTroopsPriorityList[index2].Item3;
            index1 = index2;
          }
        }
        if (index1 != -1)
        {
          (FlattenedTroopRosterElement, MapEventParty, float) readyTroopsPriority = this._readyTroopsPriorityList[index1];
          UniqueTroopDescriptor descriptor = readyTroopsPriority.Item1.Descriptor;
          MapEventParty mapEventParty = readyTroopsPriority.Item2;
          this._readyTroopsPriorityList[index1] = (FlattenedTroopRosterElement.DefaultFlattenedTroopRosterElement, (MapEventParty) null, float.MinValue);
          if (customAllocationConditions == null || customAllocationConditions(descriptor, mapEventParty))
          {
            troopsList.Add(descriptor);
            this._allocatedTroops.Add(descriptor, mapEventParty);
            --num1;
            if (this.BattleObserver != null)
            {
              IBattleObserver battleObserver = this.BattleObserver;
              int missionSide = (int) this.MissionSide;
              PartyBase party1 = mapEventParty.Party;
              FlattenedTroopRosterElement troop1 = mapEventParty.Troops[descriptor];
              CharacterObject troop2 = troop1.Troop;
              battleObserver.TroopNumberChanged((BattleSideEnum) missionSide, (IBattleCombatant) party1, (BasicCharacterObject) troop2, 1);
              if (this._troopUpgradeTracker == null)
                this._troopUpgradeTracker = new TroopUpgradeTracker();
              TroopUpgradeTracker troopUpgradeTracker = this._troopUpgradeTracker;
              PartyBase party2 = mapEventParty.Party;
              troop1 = mapEventParty.Troops[descriptor];
              CharacterObject troop3 = troop1.Troop;
              troopUpgradeTracker.AddTrackedTroop(party2, troop3);
            }
          }
        }
        else
          break;
      }
      this._readyTroopsPriorityList.RemoveAll((Predicate<(FlattenedTroopRosterElement, MapEventParty, float)>) (i => (double) i.Item3 <= -3.4028234663852886E+38));
      this._requiresTroopCacheUpdate = true;
    }

    public void GetAllTroops(ref List<UniqueTroopDescriptor> troopsList)
    {
      if (troopsList == null)
        troopsList = new List<UniqueTroopDescriptor>();
      else
        troopsList.Clear();
      foreach ((FlattenedTroopRosterElement, MapEventParty, float) readyTroopsPriority in this._readyTroopsPriorityList)
        troopsList.Add(readyTroopsPriority.Item1.Descriptor);
      foreach (UniqueTroopDescriptor key in this._allocatedTroops.Keys)
        troopsList.Add(key);
    }

    public CharacterObject GetAllocatedTroop(UniqueTroopDescriptor troopDesc0)
    {
      MapEventParty mapEventParty;
      return this._allocatedTroops.TryGetValue(troopDesc0, out mapEventParty) ? mapEventParty.Troops[troopDesc0].Troop : (CharacterObject) null;
    }

    public CharacterObject GetReadyTroop(UniqueTroopDescriptor troopDesc0)
    {
      this.CheckReadyTroopsTemporaryCache();
      MapEventParty mapEventParty;
      return this._readyTroopsTemporaryCache.TryGetValue(troopDesc0, out mapEventParty) ? mapEventParty.Troops[troopDesc0].Troop : (CharacterObject) null;
    }

    public PartyBase GetAllocatedTroopParty(UniqueTroopDescriptor troopDescriptor)
    {
      MapEventParty mapEventParty;
      return this._allocatedTroops.TryGetValue(troopDescriptor, out mapEventParty) ? mapEventParty.Party : (PartyBase) null;
    }

    public PartyBase GetReadyTroopParty(UniqueTroopDescriptor troopDescriptor)
    {
      this.CheckReadyTroopsTemporaryCache();
      MapEventParty mapEventParty;
      return this._readyTroopsTemporaryCache.TryGetValue(troopDescriptor, out mapEventParty) ? mapEventParty.Party : (PartyBase) null;
    }

    public void OnTroopWounded(UniqueTroopDescriptor troopDesc1)
    {
      MapEventParty allocatedTroop = this._allocatedTroops[troopDesc1];
      allocatedTroop.OnTroopWounded(troopDesc1);
      this.CasualtyStrength += Campaign.Current.Models.MilitaryPowerModel.GetTroopPower(allocatedTroop.GetTroop(troopDesc1), this.MissionSide, this.MapEvent.SimulationContext, this.LeaderSimulationModifier);
      ++this.Casualties;
    }

    public void OnTroopKilled(UniqueTroopDescriptor troopDesc1)
    {
      MapEventParty allocatedTroop = this._allocatedTroops[troopDesc1];
      allocatedTroop.OnTroopKilled(troopDesc1);
      this.CasualtyStrength += Campaign.Current.Models.MilitaryPowerModel.GetTroopPower(allocatedTroop.GetTroop(troopDesc1), this.MissionSide, this.MapEvent.SimulationContext, this.LeaderSimulationModifier);
      ++this.Casualties;
    }

    public void OnTroopRouted(UniqueTroopDescriptor troopDesc1)
    {
      MapEventParty allocatedTroop = this._allocatedTroops[troopDesc1];
      allocatedTroop.OnTroopRouted(troopDesc1);
      this.CasualtyStrength += Campaign.Current.Models.MilitaryPowerModel.GetTroopPower(allocatedTroop.GetTroop(troopDesc1), this.MissionSide, this.MapEvent.SimulationContext, this.LeaderSimulationModifier) * 0.1f;
    }

    public void OnTroopScoreHit(
      UniqueTroopDescriptor troopDesc1,
      CharacterObject attackedTroop,
      int damage,
      bool isFatal,
      bool isTeamKill,
      WeaponComponentData attackerWeapon,
      bool isSimulatedHit)
    {
      this._allocatedTroops[troopDesc1].OnTroopScoreHit(troopDesc1, attackedTroop, damage, isFatal, isTeamKill, attackerWeapon, isSimulatedHit);
    }

    private void MakeReady(
      bool includeHumanPlayers,
      FlattenedTroopRoster priorTroops = null,
      int sizeOfSide = -1)
    {
      if (this._readyTroopsPriorityList == null || this._allocatedTroops == null)
      {
        this._readyTroopsPriorityList = new List<(FlattenedTroopRosterElement, MapEventParty, float)>();
        this._allocatedTroops = new Dictionary<UniqueTroopDescriptor, MapEventParty>();
      }
      else
      {
        this._readyTroopsPriorityList.Clear();
        this._allocatedTroops.Clear();
      }
      if (sizeOfSide <= 0)
      {
        sizeOfSide = 0;
        foreach (MapEventParty battleParty in (List<MapEventParty>) this._battleParties)
          sizeOfSide += battleParty.Party.NumberOfHealthyMembers;
      }
      foreach (MapEventParty battleParty in (List<MapEventParty>) this._battleParties)
        this.MakeReadyParty(battleParty, priorTroops, includeHumanPlayers, sizeOfSide);
      this._requiresTroopCacheUpdate = true;
    }

    private void MakeReadyParty(
      MapEventParty battleParty,
      FlattenedTroopRoster priorityTroops,
      bool includePlayers,
      int sizeOfSide)
    {
      battleParty.Update();
      bool forcePriorityTroops = priorityTroops != null && this._mapEvent.PlayerSide == this.MissionSide && (this._mapEvent.IsHideoutBattle || this._mapEvent.IsSiegeAssault && PlayerSiege.BesiegedSettlement != null && PlayerSiege.BesiegedSettlement.CurrentSiegeState == Settlement.SiegeState.InTheLordsHall);
      Campaign.Current.Models.TroopSupplierProbabilityModel.EnqueueTroopSpawnProbabilitiesAccordingToUnitSpawnPrioritization(battleParty, priorityTroops, includePlayers, sizeOfSide, forcePriorityTroops, this._readyTroopsPriorityList);
    }

    private void CheckReadyTroopsTemporaryCache()
    {
      if (this._readyTroopsTemporaryCache == null)
        this._readyTroopsTemporaryCache = new Dictionary<UniqueTroopDescriptor, MapEventParty>();
      if (!this._requiresTroopCacheUpdate)
        return;
      this._readyTroopsTemporaryCache.Clear();
      foreach ((FlattenedTroopRosterElement, MapEventParty, float) readyTroopsPriority in this._readyTroopsPriorityList)
        this._readyTroopsTemporaryCache.Add(readyTroopsPriority.Item1.Descriptor, readyTroopsPriority.Item2);
      this._requiresTroopCacheUpdate = false;
    }

    public void MakeReadyForSimulation(FlattenedTroopRoster priorTroops, int sizeOfSide = -1)
    {
      this.MakeReady(false, priorTroops, sizeOfSide);
      this.AllocateTroops(ref this._simulationTroopList, sizeOfSide);
    }

    public void MakeReadyForMission(FlattenedTroopRoster priorTroops)
    {
      this.MakeReady(true, priorTroops);
    }

    public void EndSimulation()
    {
      this._simulationTroopList.Clear();
      this._readyTroopsPriorityList.Clear();
      this._requiresTroopCacheUpdate = true;
      this._allocatedTroops.Clear();
    }

    internal void ResetContributionToBattleToStrength()
    {
      foreach (MapEventParty battleParty in (List<MapEventParty>) this._battleParties)
        battleParty.ResetContributionToBattleToStrength();
    }

    internal void CollectAll(LootCollector lootCollector, out bool playerCaptured)
    {
      playerCaptured = false;
      bool flag1 = false;
      ExplainedNumber bonuses = new ExplainedNumber(1f);
      float num1 = 0.0f;
      foreach (MapEventParty party in (List<MapEventParty>) this.OtherSide.Parties)
      {
        if (party != null)
        {
          bool? nullable = party.Party?.MobileParty?.HasPerk(DefaultPerks.Roguery.KnowHow);
          bool flag2 = true;
          if (nullable.GetValueOrDefault() == flag2 & nullable.HasValue)
            flag1 = true;
        }
        if (party?.Party?.LeaderHero != null && party.Party.LeaderHero.GetPerkValue(DefaultPerks.Roguery.RogueExtraordinaire) && (double) num1 < (double) party.Party.LeaderHero.GetSkillValue(DefaultSkills.Roguery))
        {
          num1 = (float) party.Party.LeaderHero.GetSkillValue(DefaultSkills.Roguery);
          PerkHelper.AddEpicPerkBonusForCharacter(DefaultPerks.Roguery.RogueExtraordinaire, party.Party.LeaderHero.CharacterObject, DefaultSkills.Roguery, true, ref bonuses, Campaign.Current.Models.CharacterDevelopmentModel.MinSkillRequiredForEpicPerkBonus);
        }
      }
      foreach (MapEventParty battleParty in (List<MapEventParty>) this._battleParties)
      {
        PartyBase party = battleParty.Party;
        MapEventSide.CaptureWoundedTroops(lootCollector, party, this.IsSurrendered, ref playerCaptured);
        lootCollector.LootedPrisoners.Add(party.PrisonRoster);
        bool flag3 = false;
        for (int index = party.PrisonRoster.Count - 1; index >= 0; --index)
        {
          TroopRosterElement troopRosterElement = party.PrisonRoster.data[index];
          if (!troopRosterElement.Character.IsHero)
          {
            party.PrisonRoster.RemoveTroop(troopRosterElement.Character, troopRosterElement.Number);
            flag3 = true;
          }
        }
        if (flag3)
          party.PrisonRoster.RemoveZeroCounts();
        float num2 = 0.5f * bonuses.ResultNumber;
        if (party.IsMobile)
        {
          if (flag1 && (party.MobileParty.IsCaravan || party.MobileParty.IsVillager))
            num2 *= 1f + DefaultPerks.Roguery.KnowHow.PrimaryBonus;
        }
        else if (party.IsSettlement)
        {
          Settlement settlement = party.Settlement;
          num2 = !settlement.IsTown ? (!settlement.IsVillage ? 1f : ((double) settlement.SettlementHitPoints > 0.0 ? 0.0f : 1f)) : 0.0f;
        }
        float num3 = 1.0 > (double) num2 ? num2 : 1f;
        if (party == PartyBase.MainParty)
        {
          MBList<ItemRosterElement> mbList = party.ItemRoster.Where<ItemRosterElement>((Func<ItemRosterElement, bool>) (x => x.EquipmentElement.Item.IsMountable)).ToMBList<ItemRosterElement>();
          mbList.Shuffle<ItemRosterElement>();
          Dictionary<ItemRosterElement, int> dictionary = new Dictionary<ItemRosterElement, int>();
          int num4 = 0;
          foreach (ItemRosterElement key in (List<ItemRosterElement>) mbList)
          {
            int num5 = MathF.Min(key.Amount, 3 - num4);
            dictionary.Add(key, num5);
            num4 += num5;
            if (num4 == 3)
              break;
          }
          ItemRoster itemRoster = new ItemRoster(party.ItemRoster);
          for (int index = 0; index < itemRoster.Count; ++index)
          {
            ItemRosterElement key = itemRoster[index];
            if (!key.EquipmentElement.Item.NotMerchandise && !key.EquipmentElement.IsQuestItem && !key.EquipmentElement.Item.IsBannerItem)
            {
              int num6;
              dictionary.TryGetValue(key, out num6);
              int num7 = key.Amount - num6;
              if (num7 > 0)
              {
                int number = MBRandom.RoundRandomized((float) num7 * num3);
                lootCollector.AddLootedItems(party, key.EquipmentElement, number);
                party.ItemRoster.AddToCounts(key.EquipmentElement, -num7);
              }
            }
          }
        }
        else
        {
          ItemRoster itemRoster = new ItemRoster(party.ItemRoster);
          for (int index = 0; index < itemRoster.Count; ++index)
          {
            ItemRosterElement itemRosterElement = itemRoster[index];
            if (!itemRosterElement.EquipmentElement.Item.NotMerchandise && !itemRosterElement.EquipmentElement.IsQuestItem)
            {
              int number = MBRandom.RoundRandomized((float) ((double) itemRosterElement.Amount * (double) num3 * (itemRosterElement.EquipmentElement.Item.IsMountable ? 0.33000001311302185 : 1.0)));
              if (number > 0)
              {
                lootCollector.AddLootedItems(party, itemRosterElement.EquipmentElement, number);
                party.ItemRoster.AddToCounts(itemRosterElement.EquipmentElement, -number);
              }
            }
          }
        }
        lootCollector.CasualtiesInBattle.Add(battleParty.DiedInBattle);
        lootCollector.CasualtiesInBattle.Add(battleParty.WoundedInBattle);
        MapEventSide.OnPartyDefeated(party);
      }
    }

    private static void OnPartyDefeated(PartyBase defeatedParty)
    {
      if (!defeatedParty.IsMobile)
        return;
      defeatedParty.MobileParty.RecentEventsMorale += Campaign.Current.Models.PartyMoraleModel.GetDefeatMoraleChange(defeatedParty);
      if (defeatedParty.NumberOfHealthyMembers <= 0 || defeatedParty.IsMobile && defeatedParty.MobileParty.IsGarrison)
        return;
      defeatedParty.MobileParty.Position2D = defeatedParty.MobileParty.CurrentSettlement == null ? MobilePartyHelper.FindReachablePointAroundPosition(defeatedParty.MobileParty.Position2D, 4f, 3f) : defeatedParty.MobileParty.CurrentSettlement.GatePosition;
      defeatedParty.MobileParty.Ai.ForceDefaultBehaviorUpdate();
    }

    private static void CaptureWoundedTroops(
      LootCollector lootCollector,
      PartyBase defeatedParty,
      bool isSurrender,
      ref bool playerCaptured)
    {
      MapEventSide.CaptureRegularTroops(lootCollector, defeatedParty, isSurrender);
      if (defeatedParty == PartyBase.MainParty)
      {
        bool playerCaptured1;
        MapEventSide.CaptureWoundedHeroesForMainParty(lootCollector, defeatedParty, isSurrender, out playerCaptured1);
        if (playerCaptured1)
          playerCaptured = true;
      }
      else if (defeatedParty.LeaderHero != null)
        MapEventSide.CaptureWoundedHeroes(lootCollector, defeatedParty, isSurrender);
      defeatedParty.MemberRoster.RemoveZeroCounts();
    }

    private static void CaptureWoundedHeroesForMainParty(
      LootCollector lootCollector,
      PartyBase defeatedParty,
      bool isSurrender,
      out bool playerCaptured)
    {
      playerCaptured = false;
      bool flag = false;
      if (defeatedParty != PartyBase.MainParty)
      {
        foreach (TroopRosterElement troopRosterElement in (List<TroopRosterElement>) defeatedParty.MemberRoster.GetTroopRoster())
        {
          if (troopRosterElement.Character != null && troopRosterElement.Character.IsHero && !troopRosterElement.Character.HeroObject.IsWounded)
            flag = true;
        }
      }
      if (!(!flag | isSurrender))
        return;
      playerCaptured = true;
      for (int index = 0; index < defeatedParty.MemberRoster.Count; ++index)
      {
        TroopRosterElement elementCopyAtIndex = defeatedParty.MemberRoster.GetElementCopyAtIndex(index);
        if (elementCopyAtIndex.Character.IsHero)
        {
          if (elementCopyAtIndex.Character.HeroObject.DeathMark != KillCharacterAction.KillCharacterActionDetail.DiedInBattle)
          {
            defeatedParty.MemberRoster.AddToCountsAtIndex(index, -1, removeDepleted: false);
            if (elementCopyAtIndex.Character.HeroObject != Hero.MainHero && (double) MBRandom.RandomFloat < 0.5)
              MakeHeroFugitiveAction.Apply(elementCopyAtIndex.Character.HeroObject);
            else if (!elementCopyAtIndex.Character.HeroObject.IsDead && elementCopyAtIndex.Character.HeroObject.DeathMark == KillCharacterAction.KillCharacterActionDetail.None)
              lootCollector.LootedMembers.AddToCounts(elementCopyAtIndex.Character, 1);
            if (defeatedParty.LeaderHero == elementCopyAtIndex.Character.HeroObject && defeatedParty.IsMobile)
              defeatedParty.MobileParty.RemovePartyLeader();
          }
        }
        else if (elementCopyAtIndex.Number > 0)
        {
          defeatedParty.MemberRoster.AddToCountsAtIndex(index, -elementCopyAtIndex.Number, removeDepleted: false);
          lootCollector.LootedMembers.AddToCounts(elementCopyAtIndex.Character, elementCopyAtIndex.Number);
        }
      }
    }

    private static void CaptureRegularTroops(
      LootCollector lootCollector,
      PartyBase defeatedParty,
      bool isSurrender)
    {
      for (int index = 0; index < defeatedParty.MemberRoster.Count; ++index)
      {
        TroopRosterElement elementCopyAtIndex = defeatedParty.MemberRoster.GetElementCopyAtIndex(index);
        if (!elementCopyAtIndex.Character.IsHero && (elementCopyAtIndex.WoundedNumber > 0 || isSurrender && elementCopyAtIndex.Number > 0))
        {
          int count = isSurrender ? elementCopyAtIndex.Number : elementCopyAtIndex.WoundedNumber;
          lootCollector.LootedMembers.AddToCounts(elementCopyAtIndex.Character, count);
          defeatedParty.MemberRoster.AddToCountsAtIndex(index, -count, -elementCopyAtIndex.WoundedNumber, removeDepleted: false);
        }
      }
    }

    private static void CaptureWoundedHeroes(
      LootCollector lootCollector,
      PartyBase defeatedParty,
      bool isSurrender)
    {
      if (!(defeatedParty.LeaderHero.IsWounded | isSurrender))
        return;
      for (int index = 0; index < defeatedParty.MemberRoster.Count; ++index)
      {
        TroopRosterElement elementCopyAtIndex = defeatedParty.MemberRoster.GetElementCopyAtIndex(index);
        if (elementCopyAtIndex.Character.IsHero)
        {
          if (elementCopyAtIndex.Character.HeroObject.DeathMark != KillCharacterAction.KillCharacterActionDetail.DiedInBattle)
          {
            lootCollector.LootedMembers.AddToCounts(elementCopyAtIndex.Character, 1);
            if (defeatedParty.LeaderHero == elementCopyAtIndex.Character.HeroObject && defeatedParty.IsMobile)
              defeatedParty.MobileParty.RemovePartyLeader();
            defeatedParty.MemberRoster.AddToCountsAtIndex(index, -1, removeDepleted: false);
          }
        }
        else if (elementCopyAtIndex.Number > 0)
        {
          lootCollector.LootedMembers.AddToCounts(elementCopyAtIndex.Character, elementCopyAtIndex.Number);
          defeatedParty.MemberRoster.AddToCountsAtIndex(index, -elementCopyAtIndex.Number, removeDepleted: false);
        }
      }
    }

    public ItemRoster ItemRosterForPlayerLootShare(PartyBase playerParty)
    {
      return this._battleParties[this._battleParties.FindIndexQ<MapEventParty>((Func<MapEventParty, bool>) (p => p.Party == playerParty))].RosterToReceiveLootItems;
    }

    public TroopRoster MemberRosterForPlayerLootShare(PartyBase playerParty)
    {
      return this._battleParties[this._battleParties.FindIndexQ<MapEventParty>((Func<MapEventParty, bool>) (p => p.Party == playerParty))].RosterToReceiveLootMembers;
    }

    public TroopRoster PrisonerRosterForPlayerLootShare(PartyBase playerParty)
    {
      return this._battleParties[this._battleParties.FindIndexQ<MapEventParty>((Func<MapEventParty, bool>) (p => p.Party == playerParty))].RosterToReceiveLootPrisoners;
    }

    public void Clear() => this._battleParties.Clear();

    public UniqueTroopDescriptor SelectRandomSimulationTroop()
    {
      this._selectedSimulationTroopIndex = MBRandom.RandomInt(this.NumRemainingSimulationTroops);
      this._selectedSimulationTroopDescriptor = this._simulationTroopList[this._selectedSimulationTroopIndex];
      this._selectedSimulationTroop = this.GetAllocatedTroop(this._selectedSimulationTroopDescriptor);
      return this._selectedSimulationTroopDescriptor;
    }

    private void RemoveSelectedTroopFromSimulationList()
    {
      this._simulationTroopList[this._selectedSimulationTroopIndex] = this._simulationTroopList[this._simulationTroopList.Count - 1];
      this._simulationTroopList.RemoveAt(this._simulationTroopList.Count - 1);
      this._selectedSimulationTroopIndex = -1;
      this._selectedSimulationTroopDescriptor = UniqueTroopDescriptor.Invalid;
      this._selectedSimulationTroop = (CharacterObject) null;
    }

    internal bool ApplySimulationDamageToSelectedTroop(
      int damage,
      DamageTypes damageType,
      PartyBase strikerParty)
    {
      bool selectedTroop = false;
      if (this._selectedSimulationTroop.IsHero)
      {
        this.AddHeroDamage(this._selectedSimulationTroop.HeroObject, damage);
        if (this._selectedSimulationTroop.HeroObject.IsWounded)
        {
          if ((double) MBRandom.RandomFloat > (double) Campaign.Current.Models.PartyHealingModel.GetSurvivalChance(this._selectedSimulationTroop.HeroObject.PartyBelongedTo?.Party ?? (PartyBase) null, this._selectedSimulationTroop, damageType, false, strikerParty) && this._selectedSimulationTroop.HeroObject.CanDie(KillCharacterAction.KillCharacterActionDetail.DiedInBattle))
          {
            this.OnTroopKilled(this._selectedSimulationTroopDescriptor);
            this.BattleObserver?.TroopNumberChanged(this.MissionSide, (IBattleCombatant) this.GetAllocatedTroopParty(this._selectedSimulationTroopDescriptor), (BasicCharacterObject) this._selectedSimulationTroop, -1, 1);
            KillCharacterAction.ApplyByBattle(this._selectedSimulationTroop.HeroObject, (Hero) null, false);
          }
          else
          {
            this.OnTroopWounded(this._selectedSimulationTroopDescriptor);
            this.BattleObserver?.TroopNumberChanged(this.MissionSide, (IBattleCombatant) this.GetAllocatedTroopParty(this._selectedSimulationTroopDescriptor), (BasicCharacterObject) this._selectedSimulationTroop, -1, numberWounded: 1);
          }
          selectedTroop = true;
        }
      }
      else if (MBRandom.RandomInt(this._selectedSimulationTroop.MaxHitPoints()) < damage)
      {
        PartyBase party = this._allocatedTroops[this._selectedSimulationTroopDescriptor].Party;
        if ((double) MBRandom.RandomFloat < (double) Campaign.Current.Models.PartyHealingModel.GetSurvivalChance(party, this._selectedSimulationTroop, damageType, false, strikerParty))
        {
          this.OnTroopWounded(this._selectedSimulationTroopDescriptor);
          this.BattleObserver?.TroopNumberChanged(this.MissionSide, (IBattleCombatant) this.GetAllocatedTroopParty(this._selectedSimulationTroopDescriptor), (BasicCharacterObject) this._selectedSimulationTroop, -1, numberWounded: 1);
          SkillLevelingManager.OnSurgeryApplied(party.MobileParty, true, this._selectedSimulationTroop.Tier);
          if (strikerParty?.MobileParty != null && strikerParty.MobileParty.HasPerk(DefaultPerks.Medicine.DoctorsOath))
            SkillLevelingManager.OnSurgeryApplied(strikerParty.MobileParty, true, this._selectedSimulationTroop.Tier);
        }
        else
        {
          this.OnTroopKilled(this._selectedSimulationTroopDescriptor);
          this.BattleObserver?.TroopNumberChanged(this.MissionSide, (IBattleCombatant) this.GetAllocatedTroopParty(this._selectedSimulationTroopDescriptor), (BasicCharacterObject) this._selectedSimulationTroop, -1, 1);
          SkillLevelingManager.OnSurgeryApplied(party.MobileParty, false, this._selectedSimulationTroop.Tier);
          if (strikerParty?.MobileParty != null && strikerParty.MobileParty.HasPerk(DefaultPerks.Medicine.DoctorsOath))
            SkillLevelingManager.OnSurgeryApplied(strikerParty.MobileParty, false, this._selectedSimulationTroop.Tier);
        }
        selectedTroop = true;
      }
      if (selectedTroop)
        this.RemoveSelectedTroopFromSimulationList();
      return selectedTroop;
    }

    public void ApplySimulatedHitRewardToSelectedTroop(
      CharacterObject strikerTroop,
      CharacterObject attackedTroop,
      int damage,
      bool isFinishingStrike)
    {
      EquipmentElement equipmentElement = strikerTroop.FirstBattleEquipment[EquipmentIndex.WeaponItemBeginSlot];
      this.OnTroopScoreHit(this._selectedSimulationTroopDescriptor, attackedTroop, damage, isFinishingStrike, false, equipmentElement.Item?.PrimaryWeapon, true);
      PartyBase party = this._allocatedTroops[this._selectedSimulationTroopDescriptor].Party;
      if (isFinishingStrike && (!attackedTroop.IsHero || !attackedTroop.HeroObject.IsDead))
        SkillLevelingManager.OnSimulationCombatKill(this._selectedSimulationTroop, attackedTroop, party, this.LeaderParty);
      if (this.BattleObserver == null)
        return;
      if (isFinishingStrike)
        this.BattleObserver.TroopNumberChanged(this.MissionSide, (IBattleCombatant) party, (BasicCharacterObject) this._selectedSimulationTroop, killCount: 1);
      if (this._selectedSimulationTroop.IsHero)
      {
        foreach (SkillObject skill in this._troopUpgradeTracker.CheckSkillUpgrades(this._selectedSimulationTroop.HeroObject).ToList<SkillObject>())
          this.BattleObserver.HeroSkillIncreased(this.MissionSide, (IBattleCombatant) party, (BasicCharacterObject) this._selectedSimulationTroop, skill);
      }
      else
      {
        int numberReadyToUpgrade = this._troopUpgradeTracker.CheckUpgradedCount(party, this._selectedSimulationTroop);
        if (numberReadyToUpgrade == 0)
          return;
        this.BattleObserver.TroopNumberChanged(this.MissionSide, (IBattleCombatant) party, (BasicCharacterObject) this._selectedSimulationTroop, numberReadyToUpgrade: numberReadyToUpgrade);
      }
    }

    public void Surrender()
    {
      MapEventSide.SurrenderParty(this.LeaderParty);
      this.IsSurrendered = true;
    }

    private static void SurrenderParty(PartyBase party)
    {
      for (int index = 0; index < party.MemberRoster.Count; ++index)
      {
        TroopRosterElement elementCopyAtIndex = party.MemberRoster.GetElementCopyAtIndex(index);
        if (!elementCopyAtIndex.Character.IsHero)
          party.MemberRoster.AddToCountsAtIndex(index, 0, elementCopyAtIndex.Number - elementCopyAtIndex.WoundedNumber);
      }
    }

    internal void AddNearbyPartyToPlayerMapEvent(MobileParty party)
    {
      if (party.MapEventSide == this)
        return;
      party.MapEventSide = this;
      this._nearbyPartiesAddedToPlayerMapEvent.Add(party);
      CampaignEventDispatcher.Instance.OnNearbyPartyAddedToPlayerMapEvent(party);
    }

    internal void RemoveNearbyPartiesFromPlayerMapEvent()
    {
      foreach (MobileParty mobileParty in (List<MobileParty>) this._nearbyPartiesAddedToPlayerMapEvent)
        mobileParty.MapEventSide = (MapEventSide) null;
      this._nearbyPartiesAddedToPlayerMapEvent.Clear();
    }
  }
}
